import codecs
import gzip
import stat
import json
import os
import random
import re
import shlex
import subprocess
import time
import traceback
from pprint import pprint
from urllib import parse
from io import StringIO
from lxml.etree import HTMLParser
from util.lib.OS import OS
from util.lib.STD import STD
from util.lib.TIME import TIME
from util.lib.TYPE import TYPE


class Util:
    @staticmethod
    def is_all_zero(_set):
        for val in _set:
            if val != 0:
                return False
        return True

    @staticmethod
    def err(e=None):
        if e:
            print(e)
        STD.flush(traceback.format_exc())

    @staticmethod
    def check_gzip(data, keyword):
        if data.find(keyword) is -1:
            data = Util.gzDecode(data)
        return data

    @staticmethod
    def quote(data):
        return parse.quote(data)

    @staticmethod
    def print_l(string):
        print(string + "\n")

    @staticmethod
    def replace_space(string):
        return string.replace('\r', '').replace('\n', '').replace(' ', '')

    @staticmethod
    def trim_all(string):
        return Util.replace_space(str(string))

    @staticmethod
    def trim(string):
        return string.lstrip(' \n\r\n').rstrip(' \n\r\n')

    @staticmethod
    def has_other_country_letter(string):
        res = re.search(r"[\u2E80-\uFFFDh]", string)
        if res is not None:
            return True
        else:
            return False

    @staticmethod
    def get_random_ip():
        ip_list = list()
        ip_list.append(str(random.randint(2, 120)))
        ip_list.append(str(random.randint(10, 254)))
        ip_list.append(str(random.randint(10, 254)))
        ip_list.append(str(random.randint(10, 254)))
        return '.'.join(ip_list)

    @staticmethod
    def gzDecode(data):
        stream = StringIO(data)
        result = gzip.GzipFile(fileobj=stream)
        return result.read()

    file_dict = dict()

    @staticmethod
    def append_data(file_path, content):
        if file_path not in Util.file_dict or Util.file_dict[file_path].closed or not Util.file_dict[file_path]:
            Util.file_dict[file_path] = OS.open(file_path, "a")
        Util.file_dict[file_path].write(content)
        Util.file_dict[file_path].flush()

    @staticmethod
    def all_file_flush():
        for file_path in Util.file_dict:
            if Util.file_dict[file_path].closed:
                Util.file_dict[file_path] = OS.open(file_path, "a")
            Util.file_dict[file_path].flush()

    @staticmethod
    def Log(tag, data):
        print(str(tag) + ':')
        pprint(data)

    @staticmethod
    def tup_join(tup, split=''):
        string = ""
        length = len(tup)
        count = 0
        for item in tup:
            count += 1
            string += str(item) + ('' if count >= length else split)
        return string

    @staticmethod
    def join(arr, split=''):
        if TYPE.is_tuple(arr):
            return Util.tup_join(arr, split)
        elif TYPE.is_list(arr):
            return split.join(arr)
        else:
            STD.out()
            print("")

    @staticmethod
    def read_each_line(file_path, func=None):
        if func is None:
            return
        fp = OS.open(file_path, "r")
        for line in fp:
            func(line)
        fp.close()

    @staticmethod
    def once_read_cnt(fName, read_size):
        f = OS.open(fName, "r")
        while True:
            block = f.read(read_size)
            if block:
                yield block
            else:
                f.close()
                return

    @staticmethod
    def read_reverse(file_path, func=None):
        file_no_break_path = file_path + '_no_break'
        OS.open(file_no_break_path, 'w').close()
        once_read_size = 3333333
        f = OS.open(file_no_break_path, 'a')
        for block in Util.once_read_cnt(file_path, once_read_size):
            block = block.replace("\n", '{{{')
            f.write(block)
        f.close()

        f = codecs.open(file_no_break_path, encoding='utf-8')
        f.seek(0, os.SEEK_END)
        last_position = f.tell()
        last = ''
        val = 1
        isEnd = False
        time1 = TIME.time()
        content = ''
        while 1:
            last_position -= val
            if last_position < 0:
                val = val + last_position
                last_position = 0
                isEnd = True
            f.seek(last_position)
            try:
                print('content:' + content)
                content = f.read(val)
            except:
                Util.err('content:' + content)
                exit()
            _list = content.split('{{{')
            _list.reverse()
            _list[0] = _list[0] + last
            last = _list.pop()
            func(_list)
            if isEnd:
                func([last])
                break
        f.close()
        print('kill_time:' + str(int(time.time()) - time1))
        os.remove(file_path)
        os.remove(file_no_break_path)

    @staticmethod
    def get_key(key, dic, default=''):
        try:
            if TYPE.is_tuple(dic):
                return dic[key] or default
            if (TYPE.is_list(key) and len(key) <= 1) or TYPE.is_str(key) or TYPE.is_int(key):
                if TYPE.is_list(key):
                    key = str(key[0])
                if key in dic:
                    return dic[key]
                else:
                    return default
            else:
                dic = Util.get_key(key[0], dic)
                if TYPE.is_dict(dic):
                    key.pop(0)
                    return Util.get_key(key, dic)
                else:
                    return default
        except:
            return default

    @staticmethod
    def kill_repeat(split_char, path_old, index_arr=None):
        if index_arr is None:
            index_arr = [0]
        print('kill_path:' + path_old)
        if path_old in Util.file_dict:
            Util.file_dict[path_old].close()
        lines = set()
        need_key = len(index_arr)
        path_new = path_old + '_repeat'
        if os.path.exists(path_old):
            OS.open(path_new, 'w').close()
            OS.rm_file(path_new)
            os.rename(path_old, path_new)
        else:
            return lines

        start = TIME.time()

        new = OS.open(path_new)
        old = OS.open(path_old, 'a')

        def add_file():
            now_file = OS.open('tmp_' + str(now_index), 'w')
            now_file.write(chr(5).join(stack))
            now_file.close()

        stack = []
        now_index = 0
        count = 0
        for line in new:
            stack.append(line)
            if count > 200000:
                count = 0
                add_file()
                now_index += 1
                stack = []
            count += 1
        add_file()

        max_index = now_index

        def handle_line(_line):
            if len(_line) < 2:
                return
            lineArr = _line.split(split_char)
            try:
                if need_key > 1:
                    unique_id = Util.get_unique_id(lineArr[index_arr[0]], lineArr[index_arr[1]])
                else:
                    unique_id = Util.get_unique_id(lineArr[index_arr[0]])
            except:
                Util.err(_line)
                return
            if unique_id not in lines:
                lines.add(unique_id)
                old.write(_line)

        while now_index >= 0:
            stack = OS.open('tmp_' + str(now_index), 'r').read().split(chr(5))
            while len(stack) > 0:
                handle_line(stack.pop())
            now_index -= 1

        old.close()
        new.close()

        for i in range(max_index, -1, -1):
            print('./tmp_' + str(i))
            os.remove('./tmp_' + str(i))
        print(TIME.time() - start)

        os.remove(path_new)
        return lines

    @staticmethod
    def get_unique_id(small, big=''):
        if big != '':
            big_list = map(lambda x: int(x), str(big).split('|'))
            big = sum(big_list)
            big = int(str(big) + '000000000000000')
            return big + int(small)
        else:
            return int(small)

    IS_TEST = None

    @staticmethod
    def is_test():
        if Util.IS_TEST is not None:
            return Util.IS_TEST

        try:
            res = Util.cmd('uname -n')
            if res['content'].find('test') > 0 or res['content'].find('localhost') >= 0:
                is_test = True
            else:
                is_test = False
        except:
            is_test = True

        if Util.IS_TEST is None:
            Util.IS_TEST = is_test

        return is_test

    @staticmethod
    def wait_echo_cmd(shell_cmd):
        p = subprocess.Popen(shell_cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
        while p.poll() is None:
            line = p.stdout.readline()
            linestring = line.strip().decode('utf-8', 'ignore')
            if len(linestring) > 1:
                print(linestring)
        try:
            returnCode = p.returncode
        except:
            returnCode = p.returncode()

        return returnCode

    @staticmethod
    def cmd(shell_cmd):
        cmd = shlex.split(shell_cmd)
        child = subprocess.Popen(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
        child.wait()
        res = map(lambda line: line.decode('utf-8', 'ignore'), child.stdout.readlines())
        res = ''.join(res)
        return {
            'code': child.returncode,
            'content': res
        }

    @staticmethod
    def cmd_mix(shell_cmd):
        cmd = "echo system('" + Util.addslashes(shell_cmd) + "');"
        cmd1 = '''php7 -r "''' + cmd + '''"'''
        try:
            res = Util.cmd(cmd1)
        except:
            cmd1 = '''php -r "''' + cmd + '''"'''
            res = Util.cmd(cmd1)
        return res

    @staticmethod
    def path_is_py(exe_file_path):
        arr = exe_file_path.split('.')
        if arr[-1:] == 'py':
            return True
        else:
            return False

    @staticmethod
    def py_specail(exe_file_path):
        add_string = '''
    import sys
    import os
    path = os.path.dirname(os.path.abspath(sys.argv[0]).replace('\\\\', '/')) + '/'
    for i in range(1, len(path.split('/')) - 2):
        sys.path.append(path + '../' * i)
    '''
        exe_file = OS.open(exe_file_path, 'r')
        lines = []
        for line in exe_file:
            if line.startswith('import sys'):
                continue
            elif line.startswith('import os'):
                continue
            elif line.startswith('path = os.path.dirname'):
                continue
            elif line.startswith('for i in range(1, len(path.spl'):
                continue
            elif line.startswith('    sys.path.append(pa'):
                continue
            else:
                lines.append(line)
        content = add_string + ''.join(lines)
        exe_file.close()
        exe_file = OS.open(exe_file_path, 'w')
        exe_file.write(content)
        exe_file.close()

    @staticmethod
    def cmd_tmp(shell_cmd):
        cmd = Util.addslashes(shell_cmd)
        cmdArr = cmd.split(' ')
        exe_file_path = os.path.abspath(cmdArr[2]).replace('\\', '/')

        # if Util.path_is_py(exe_file_path):
        #     Util.py_specail(exe_file_path)

        file_content = '''
#!/bin/sh
. /etc/profile && . ~/.bash_profile
''' + cmd
        path = '/tmp/r_' + str(random.random())
        print(path)
        f = OS.open(path, 'w')
        f.write(file_content)
        f.close()
        os.chmod(path, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO)
        res = {'code': 1}
        try:
            res = Util.cmd("sh " + path)
            print(res)
        except:
            Util.err(path)
        finally:
            OS.rm_file(path)
            return res

    @staticmethod
    def txz_root():
        return "/data/web/server/"

    @staticmethod
    def addslashes(string):
        if string.find('\\') >= 0:
            string = string.replace('\\', '')
        if string.find("'") >= 0:
            string = string.replace("'", "\\'")
            # string = string.replace("\\\\'", "\\'")
        if string.find('"') >= 0:
            string = string.replace('"', '\\"')
        if string.find('/') >= 0:
            string = string.replace('/', '\/')
        return string

    @staticmethod
    def translate_html_special_characters(string):
        html_parser = HTMLParser()
        print(dir(html_parser))
        data = '<br>'
        print(html_parser.feed(data))
        print(data)
        return html_parser.parse(string)

    @staticmethod
    def two_sentence_similar_rate(a, b):
        a_set = set()
        b_set = set()
        for letter in a:
            a_set.add(letter)
        for letter in b:
            b_set.add(letter)

        return len(a_set & b_set) / len(a)

    @staticmethod
    def get_Gjson(_input):
        res = _input
        try:
            i = 1
            for i, letter in enumerate(res[::-1]):
                if letter == '}':
                    break
            res = str(res[res.index('(') + 1:i * -1])
            return json.loads(res)
        except:
            Util.err(_input)
            return ''
